﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Web;
using System.Xml.Linq;
using DepSoft.Helpers.Extensions;
using DepSoft.Helpers.iTunes.Elements;
using DepSoft.Helpers.iTunes.Elements.Interfaces;
using DepSoft.Helpers.iTunes.Writers;

namespace DepSoft.Helpers.iTunes
{
    public class iTunesLib
    {
        #region Private members
        private List<string> _defaultiTunesPlayLists = new List<string> { Constants.PLAYLIST_AUDIOBOOKS,
                                                                          Constants.PLAYLIST_GENIUS,
                                                                          Constants.PLAYLIST_LIBRARY,
                                                                          Constants.PLAYLIST_MOVIES,
                                                                          Constants.PLAYLIST_MUSIC,
                                                                          Constants.PLAYLIST_TVSHOWS };

        private string _iTunesLibraryLocation;
        private string _invalidFileChars = new string(Path.GetInvalidFileNameChars());
        private string _invalidPathChars = new string(Path.GetInvalidPathChars());
        #endregion

        #region Public properties
        /// <summary>
        /// iTunes library location
        /// </summary>
        public string iTunesLibraryFile { get; set; }

        /// <summary>
        /// Get all available iTunesPlayLists
        /// </summary>
        //public IEnumerable<iTunesPlaylist> iTunesPlayLists { get; set; }
        public List<iTunesPlaylist> iTunesPlayLists { get; private set; }

        /// <summary>
        /// Get all available iTunesTracks
        /// </summary>
        //public IEnumerable<iTunesTrack> iTunesTracks { get; set; }
        public List<iTunesTrack> iTunesTracks { get; private set; }
        #endregion

        #region Constructor
        /// <summary>
        /// Default constructor
        /// Get the default iTunes library location
        /// </summary>
        public iTunesLib()
        {
            this.iTunesLibraryFile = Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyMusic), "iTunes"), Constants.ITUNES_LIBRARY);
            //this.ParseiTunesLibrary();
        }

        /// <summary>
        /// Overloaded constructor
        /// Instantiate the iTunes library location through a given user location
        /// </summary>
        /// <param name="iTunesLibraryFile">iTunes library location</param>
        public iTunesLib(string iTunesLibraryFile)
        {
            this.iTunesLibraryFile = iTunesLibraryFile;
            //this.ParseiTunesLibrary();
        }
        #endregion

        #region Syncing
        //Implement syncing functionality!

        //string[] filesIpod = System.IO.Directory.GetFiles(@"C:\Users\gve\Music\TestSync_ipod");
        //string[] filesDisk = System.IO.Directory.GetFiles(@"C:\Users\gve\Music\TestSync_disk");

        //var fileListEqual = from fileIpod in filesIpod
        //                join fileDisk in filesDisk
        //                on System.IO.Path.GetFileName(fileIpod) equals System.IO.Path.GetFileName(fileDisk)
        //                select System.IO.Path.GetFileName(fileIpod);

        //fileListEqual.Dump();

        //var fileListEqual2 = (from fileIpod in filesIpod
        //                      select System.IO.Path.GetFileName(fileIpod)).Intersect
        //                     (from fileDisk in filesDisk
        //                      select System.IO.Path.GetFileName(fileDisk)
        //                     );

        //fileListEqual2.Dump();

        //var fileListToExport = (from fileIpod in filesIpod
        //                        select System.IO.Path.GetFileName(fileIpod)).Except
        //                       (from fileDisk in filesDisk
        //                        select System.IO.Path.GetFileName(fileDisk)
        //                       );

        //fileListToExport.Dump();

        //var fileListToDelete = (from fileDisk in filesDisk
        //                        select System.IO.Path.GetFileName(fileDisk)).Except
        //                       (from fileIpod in filesIpod
        //                        select System.IO.Path.GetFileName(fileIpod)
        //                       );

        //fileListToDelete.Dump();
        #endregion

        #region Private methods
        /// <summary>
        /// </summary>
        /// <param name="exportLocationList"></param>
        private void CleanExportLocation(List<string> exportLocationList)
        {
            foreach (string exportLocation in exportLocationList)
            {
                if (Directory.Exists(exportLocation))
                {
                    List<string> subDirectories = Directory.GetDirectories(exportLocation).ToList();
                    this.CleanExportLocation(subDirectories);

                    foreach (string file in Directory.GetFiles(exportLocation))
                        File.Delete(file);

                    Directory.Delete(exportLocation);
                }
            }
        }

        /// <summary>
        /// </summary>
        /// <param name="tracks"></param>
        /// <param name="exportLocation"></param>
        /// <param name="playlistWriter"></param>
        private void ExportTracks(List<iTunesTrack> tracks, string exportLocation, PlaylistWriter playlistWriter)
        {
            foreach (iTunesTrack track in tracks)
            {
                playlistWriter.WriteTrack(track.Location, track);
            }
        }

        /// <summary>
        /// </summary>
        /// <param name="tracks"></param>
        /// <param name="exportLocation"></param>
        /// <param name="exportLocationName"></param>
        /// <param name="createSubFolders"></param>
        /// <param name="playlistWriter"></param>
        private void ExportTracks(List<iTunesTrack> tracks, string exportLocation, string exportLocationName, bool createSubFolders, PlaylistWriter playlistWriter)
        {
            foreach (iTunesTrack track in tracks)
            {
                //Not all tracks are located inside the main iTunes Library folder!
                string trackLocation = string.Empty;
                string trackDirectory = string.Empty;
                string destinationDirectory = string.Empty;
                if (track.Location.Contains(this._iTunesLibraryLocation))
                {
                    trackLocation = track.Location.Replace(this._iTunesLibraryLocation, string.Empty);
                    trackDirectory = createSubFolders ? Path.Combine(exportLocationName, Path.GetDirectoryName(trackLocation)) : exportLocationName;
                    destinationDirectory = Path.Combine(exportLocation, trackDirectory);
                }
                else
                {
                    //If the given tracks are not located in the iTunes main folder, create a folder inside the exportlocation with the structure Artist - Album
                    trackDirectory = createSubFolders ? Path.Combine(exportLocationName, string.Concat(string.IsNullOrEmpty(track.AlbumArtist) ? track.Artist : track.AlbumArtist, " - ", track.Album)) : exportLocationName;
                    destinationDirectory = Path.Combine(exportLocation, trackDirectory);
                }

                playlistWriter.WriteTrack(Path.Combine(destinationDirectory, track.FileName), track);
            }
        }

        /// <summary>
        /// </summary>
        /// <param name="tracks"></param>
        /// <param name="exportLocation"></param>
        /// <param name="exportLocationName"></param>
        /// <param name="createSubFolders"></param>
        /// <param name="backgroundWorker"></param>
        /// <param name="trackCountTotal"></param>
        /// <param name="trackCountCurrent"></param>
        private void ExportTracks(List<iTunesTrack> tracks, string exportLocation, string exportLocationName, bool createSubFolders, BackgroundWorker backgroundWorker, float trackCountTotal, float trackCountCurrent)
        {
            foreach (iTunesTrack track in tracks)
            {
                if (backgroundWorker.CancellationPending == true)
                    break;

                //Not all tracks are located inside the main iTunes Library folder!
                string trackLocation = string.Empty;
                string trackDirectory = string.Empty;
                string destinationDirectory = string.Empty;
                if (track.Location.Contains(this._iTunesLibraryLocation))
                {
                    trackLocation = track.Location.Replace(this._iTunesLibraryLocation, string.Empty);
                    trackDirectory = createSubFolders ? Path.Combine(exportLocationName, Path.GetDirectoryName(trackLocation)) : exportLocationName;
                    destinationDirectory = Path.Combine(exportLocation, trackDirectory);
                }
                else
                {
                    //If the given tracks are not located in the iTunes main folder, create a folder inside the exportlocation with the structure Artist - Album
                    trackDirectory = createSubFolders ? Path.Combine(exportLocationName, string.Concat(string.IsNullOrEmpty(track.AlbumArtist) ? track.Artist : track.AlbumArtist, " - ", track.Album)) : exportLocationName;
                    destinationDirectory = Path.Combine(exportLocation, trackDirectory);
                }

                int progressPercentage = (int)(trackCountCurrent / trackCountTotal * 100);
                if (progressPercentage == 0)
                    progressPercentage = 1;

                backgroundWorker.ReportProgress(progressPercentage, string.Concat(string.IsNullOrEmpty(track.AlbumArtist) ? track.Artist : track.AlbumArtist, " - ", track.Album));
                ++trackCountCurrent;

                Directory.CreateDirectory(destinationDirectory);
                try
                {
                    File.Copy(track.Location, Path.Combine(destinationDirectory, track.FileName), true);
                }
                catch (PathTooLongException ex)
                {
                    //TODO: Make sure the destination file path is not to long!
                }
            }
        }

        /// <summary>
        /// Export all tracks to the given location
        /// </summary>
        /// <param name="tracks"></param>
        /// <param name="exportLocation"></param>
        /// <param name="exportLocationName"></param>
        private void ExportTracks(List<iTunesTrack> tracks, string exportLocation, string exportLocationName, bool createSubFolders, PlaylistWriter playlistWriter, BackgroundWorker backgroundWorker, float trackCountTotal, float trackCountCurrent)
        {
            foreach (iTunesTrack track in tracks)
            {
                if (backgroundWorker.CancellationPending == true)
                    break;

                //Not all tracks are located inside the main iTunes Library folder!
                string trackLocation = string.Empty;
                string trackDirectory = string.Empty;
                string destinationDirectory = string.Empty;
                if (track.Location.Contains(this._iTunesLibraryLocation))
                {
                    trackLocation = track.Location.Replace(this._iTunesLibraryLocation, string.Empty);
                    trackDirectory = createSubFolders ? Path.Combine(exportLocationName, Path.GetDirectoryName(trackLocation)) : exportLocationName;
                    destinationDirectory = Path.Combine(exportLocation, trackDirectory);
                }
                else
                {
                    //If the given tracks are not located in the iTunes main folder, create a folder inside the exportlocation with the structure Artist - Album
                    trackDirectory = createSubFolders ? Path.Combine(exportLocationName, string.Concat(string.IsNullOrEmpty(track.AlbumArtist) ? track.Artist : track.AlbumArtist, " - ", track.Album)) : exportLocationName;
                    destinationDirectory = Path.Combine(exportLocation, trackDirectory);
                }

                int progressPercentage = (int)(trackCountCurrent / trackCountTotal * 100);
                if (progressPercentage == 0)
                    progressPercentage = 1;

                backgroundWorker.ReportProgress(progressPercentage, string.Concat(string.IsNullOrEmpty(track.AlbumArtist) ? track.Artist : track.AlbumArtist, " - ", track.Album));
                ++trackCountCurrent;

                Directory.CreateDirectory(destinationDirectory);
                try
                {
                    File.Copy(track.Location, Path.Combine(destinationDirectory, track.FileName), true);
                }
                catch (PathTooLongException ex)
                {
                    //TODO: Make sure the destination file path is not to long!
                }

                playlistWriter.WriteTrack(Path.Combine(destinationDirectory, track.FileName), track);
            }
        }

        /// <summary>
        /// </summary>
        /// <param name="exportConfiguration"></param>
        private void ExportTracks(iTunesPlaylistExportConfiguration exportConfiguration)
        {
            bool notifyBackGroundWorker = !ReferenceEquals(exportConfiguration.BackgroundWorker, null);

            var exportLocationList = (from iTunesPlaylist playlist in exportConfiguration.iTunesElementList
                                      from iTunesTrack track in playlist.iTunesTracks
                                      select new
                                      {
                                          TrackDestination = this.GetTrackDestinationDirectory(playlist.ValidPlaylistName, track, exportConfiguration),
                                          Track = track
                                      }).ToList();

            int trackCountCurrent = 1;

            foreach (var exportLocation in exportLocationList) //.ToList())
            {
                if (notifyBackGroundWorker && exportConfiguration.BackgroundWorker.CancellationPending)
                    break;

                int progressPercentage = (int)(((float)trackCountCurrent / exportLocationList.Count()) * 100);
                if (progressPercentage == 0)
                    progressPercentage = 1;

                if(notifyBackGroundWorker)
                    exportConfiguration.BackgroundWorker.ReportProgress(progressPercentage, string.Concat(string.IsNullOrEmpty(exportLocation.Track.AlbumArtist) ? exportLocation.Track.Artist : exportLocation.Track.AlbumArtist, " - ", exportLocation.Track.Album));

                ++trackCountCurrent;

                Directory.CreateDirectory(exportLocation.TrackDestination);
                try
                {
                    File.Copy(exportLocation.Track.Location, Path.Combine(exportLocation.TrackDestination, exportLocation.Track.FileName), true);
                }
                catch (PathTooLongException ex)
                {
                    //TODO: Make sure the destination file path is not to long!
                }
            }
        }

        /// <summary>
        /// </summary>
        /// <param name="exportConfiguration"></param>
        private void ExportTracks(iTunesTrackExportConfiguration exportConfiguration)
        {
            bool notifyBackGroundWorker = !ReferenceEquals(exportConfiguration.BackgroundWorker, null);

            var exportLocationList = (from iTunesTrack track in exportConfiguration.iTunesElementList
                                      select new
                                      {
                                          TrackDestination = this.GetTrackDestinationDirectory(exportConfiguration.ExportLocation, track, exportConfiguration),
                                          Track = track
                                      }).ToList();

            int trackCountCurrent = 1;

            foreach (var exportLocation in exportLocationList) //.ToList())
            {
                if (notifyBackGroundWorker && exportConfiguration.BackgroundWorker.CancellationPending)
                    break;

                int progressPercentage = (int)(((float)trackCountCurrent / exportLocationList.Count()) * 100);
                if (progressPercentage == 0)
                    progressPercentage = 1;

                if (notifyBackGroundWorker)
                    exportConfiguration.BackgroundWorker.ReportProgress(progressPercentage, string.Concat(string.IsNullOrEmpty(exportLocation.Track.AlbumArtist) ? exportLocation.Track.Artist : exportLocation.Track.AlbumArtist, " - ", exportLocation.Track.Album));

                ++trackCountCurrent;

                Directory.CreateDirectory(exportLocation.TrackDestination);
                try
                {
                    File.Copy(exportLocation.Track.Location, Path.Combine(exportLocation.TrackDestination, exportLocation.Track.FileName), true);
                }
                catch (PathTooLongException ex)
                {
                    //TODO: Make sure the destination file path is not to long!
                }
            }
        }

        /// <summary>
        /// Create M3U playlist file
        /// </summary>
        /// <param name="exportConfiguration"></param>
        private void WritePlaylistFile(iTunesPlaylistExportConfiguration exportConfiguration)
        {
            foreach (iTunesPlaylist playlist in exportConfiguration.iTunesElementList)
            {
                List<iTunesTrack> tracks = (from iTunesTrack track in playlist.iTunesTracks
                                            select track).Distinct().ToList();

                //Playlists file names can't contain any / sign, we will convert them to a - sign
                PlaylistWriter playlistWriter = new M3UExtPlaylistWriter(Path.Combine(exportConfiguration.ExportLocation, string.Concat(playlist.ValidPlaylistName, Constants.FILE_EXTENSION_M3U)), playlist);

                foreach (iTunesTrack track in tracks)
                {
                    switch (exportConfiguration.PlaylistMode)
                    {
                        case PlaylistModeEnum.Normal:
                            string trackLocation = string.Empty;
                            switch (exportConfiguration.ExportMode)
	                        {
                                case ExportModeEnum.None:
                                    //Add the complete path to the the original playlist file location
                                    trackLocation = track.Location;
                                    break;
                                default:
                                    //Add the complete path to the the playlist file location for export
                                    trackLocation = Path.Combine(GetTrackDestinationDirectory(playlist.ValidPlaylistName, track, exportConfiguration), track.FileName);
                                    break;
	                        }

                            if (!exportConfiguration.PlaylistPathRoot)
                                trackLocation = trackLocation.Replace(Path.GetPathRoot(trackLocation), string.Empty);

                            playlistWriter.WriteTrack(trackLocation, track);

                            break;
                        case PlaylistModeEnum.Minimal:
                            //Only add the trackname to the playlist file
                            playlistWriter.WriteTrack(track.FileName, track);
                            break;
                    }

                }

                playlistWriter.Close();
            }
        }

        /// <summary>
        /// Create M3U playlist file
        /// </summary>
        /// <param name="exportConfiguration"></param>
        private void WritePlaylistFile(iTunesTrackExportConfiguration exportConfiguration)
        {
            //Playlists file names can't contain any / sign, we will convert them to a - sign
            PlaylistWriter playlistWriter = new M3UExtPlaylistWriter(Path.Combine(exportConfiguration.ExportLocation, string.Concat(Constants.EXPORTLOCATIONNAME_ARTISTS, Constants.FILE_EXTENSION_M3U)), null);

            foreach (iTunesTrack track in exportConfiguration.iTunesElementList)
            {
                switch (exportConfiguration.PlaylistMode)
                {
                    case PlaylistModeEnum.Normal:
                        string trackLocation = string.Empty;
                        switch (exportConfiguration.ExportMode)
                        {
                            case ExportModeEnum.None:
                                //Add the complete path to the the original playlist file location
                                trackLocation = track.Location;
                                break;
                            default:
                                //Add the complete path to the the playlist file location for export
                                trackLocation = Path.Combine(GetTrackDestinationDirectory(exportConfiguration.ExportLocation, track, exportConfiguration), track.FileName);
                                break;
                        }

                        if (!exportConfiguration.PlaylistPathRoot)
                            trackLocation = trackLocation.Replace(Path.GetPathRoot(trackLocation), string.Empty);

                        playlistWriter.WriteTrack(trackLocation, track);

                        break;
                    case PlaylistModeEnum.Minimal:
                        //Only add the trackname to the playlist file
                        playlistWriter.WriteTrack(track.FileName, track);
                        break;
                }

            }

            playlistWriter.Close();
        }

        /// <summary>
        /// Get the destination directory to write the track to
        /// </summary>
        /// <param name="playlist"></param>
        /// <param name="track"></param>
        /// <param name="exportConfiguration"></param>
        /// <returns></returns>
        private string GetTrackDestinationDirectory(string validPlaylistName, iTunesTrack track, iTunesPlaylistExportConfiguration exportConfiguration)
        {
            //Not all tracks are located inside the main iTunes Library folder!
            string trackLocation = string.Empty;
            string trackDirectory = string.Empty;
            string destinationDirectory = string.Empty;

            if (track.Location.Contains(this._iTunesLibraryLocation))
            {
                trackLocation = track.Location.Replace(this._iTunesLibraryLocation, string.Empty);
                trackDirectory = exportConfiguration.CreateSubFolders ? Path.Combine(validPlaylistName, Path.GetDirectoryName(trackLocation)) : validPlaylistName;
                destinationDirectory = Path.Combine(exportConfiguration.ExportLocation, trackDirectory);
            }
            else
            {
                //If the given tracks are not located in the iTunes main folder, create a folder inside the exportlocation with the structure Artist - Album
                trackDirectory = exportConfiguration.CreateSubFolders ? Path.Combine(validPlaylistName, string.Concat(string.IsNullOrEmpty(track.AlbumArtist) ? track.Artist : track.AlbumArtist, " - ", track.Album)) : validPlaylistName;
                destinationDirectory = Path.Combine(exportConfiguration.ExportLocation, trackDirectory);
            }

            return destinationDirectory;
        }

        /// <summary>
        /// Get the destination directory to write the track to
        /// </summary>
        /// <param name="playlist"></param>
        /// <param name="track"></param>
        /// <param name="exportConfiguration"></param>
        /// <returns></returns>
        private string GetTrackDestinationDirectory(string exportLocationName, iTunesTrack track, iTunesTrackExportConfiguration exportConfiguration)
        {
            //Not all tracks are located inside the main iTunes Library folder!
            string trackLocation = string.Empty;
            string trackDirectory = string.Empty;
            string destinationDirectory = string.Empty;

            if (track.Location.Contains(this._iTunesLibraryLocation))
            {
                trackLocation = track.Location.Replace(this._iTunesLibraryLocation, string.Empty);
                trackDirectory = exportConfiguration.CreateSubFolders ? Path.Combine(exportLocationName, Path.GetDirectoryName(trackLocation)) : exportLocationName;
                destinationDirectory = Path.Combine(exportConfiguration.ExportLocation, trackDirectory);
            }
            else
            {
                //If the given tracks are not located in the iTunes main folder, create a folder inside the exportlocation with the structure Artist - Album
                trackDirectory = exportConfiguration.CreateSubFolders ? Path.Combine(exportLocationName, string.Concat(string.IsNullOrEmpty(track.AlbumArtist) ? track.Artist : track.AlbumArtist, " - ", track.Album)) : exportLocationName;
                destinationDirectory = Path.Combine(exportConfiguration.ExportLocation, trackDirectory);
            }

            return destinationDirectory;
        }
        #endregion

        #region Public methods
        /// <summary>
        /// Parse the given iTunes Library file
        /// It will go through all the available playlists and tracks
        /// </summary>
        public void ParseiTunesLibrary()
        {
            #region Preconditions
            //Check if the iTunes library file is present
            if (!File.Exists(this.iTunesLibraryFile))
                throw new FileNotFoundException(string.Concat(this.iTunesLibraryFile, " not found!", Environment.NewLine, "Please select other file..."));

            //Check if the iTunes library music folder is available
            string iTunesLibraryLocation = (from iTunesLibNode in XDocument.Load(this.iTunesLibraryFile).Descendants("plist").Elements("dict")
                                            from key in iTunesLibNode.Descendants("key")
                                            where key.Value.Replace(" ", "").Equals("MusicFolder")
                                            select ((XElement)key.NextNode).Value).FirstOrDefault();

            this._iTunesLibraryLocation = HttpUtility.UrlDecode(iTunesLibraryLocation.Replace(Constants.URI_LOCALHOST, string.Empty)).Replace('/', Path.DirectorySeparatorChar);

            if (!Directory.Exists(this._iTunesLibraryLocation))
                throw new DirectoryNotFoundException(string.Concat(this._iTunesLibraryLocation, " not found!"));
            #endregion

            #region Get all available tracks
            XElement trackDictionary = new XElement("Tracks", from track in XDocument.Load(this.iTunesLibraryFile).Descendants("plist").Elements("dict").Elements("dict").Elements("dict")
                                                              select new XElement("track",
                                                                  from key in track.Descendants("key")
                                                                  select new XElement(((string)key).Replace(" ", ""),
                                                                      (string)(XElement)key.NextNode)));

            this.iTunesTracks = (from track in trackDictionary.Nodes()
                                 select new iTunesTrack()
                                 {
                                     Id = ((XElement)track).Element("TrackID").ToInt(0),
                                     Album = ((XElement)track).Element("Album").ToString(string.Empty),
                                     Artist = ((XElement)track).Element("Artist").ToString(string.Empty),
                                     AlbumArtist = ((XElement)track).Element("AlbumArtist").ToString(string.Empty),
                                     BitRate = ((XElement)track).Element("BitRate").ToInt(0),
                                     Comments = ((XElement)track).Element("Comments").ToString(string.Empty),
                                     Composer = ((XElement)track).Element("Composer").ToString(string.Empty),
                                     Genre = ((XElement)track).Element("Genre").ToString(string.Empty),
                                     Kind = ((XElement)track).Element("Kind").ToString(string.Empty),
                                     //To be able to have a + signs in the track location, we need to convert it to the correct HTML code before we use the UrlDecode
                                     Location = HttpUtility.UrlDecode(((XElement)track).Element("Location").ToString(string.Empty).Replace(Constants.URI_LOCALHOST, string.Empty).Replace("+", "%2B")).Replace('/', Path.DirectorySeparatorChar),
                                     Name = ((XElement)track).Element("Name").ToString(string.Empty),
                                     PlayCount = ((XElement)track).Element("PlayCount").ToInt(0),
                                     SampleRate = ((XElement)track).Element("SampleRate").ToInt(0),
                                     Size = ((XElement)track).Element("Size").ToInt64(0),
                                     //Get the track time in total seconds
                                     TotalTime = ((XElement)track).Element("TotalTime").ToInt64(0) / 1000,
                                     TrackNumber = ((XElement)track).Element("TrackNumber").ToInt(0)
                                 }).ToList();
            #endregion

            #region Get all available playlists
            //Get all the available playlists, but don't add the PlaylistItems Node
            XElement playListDictionary = new XElement("PlayLists", from iTunesPlayList in XDocument.Load(this.iTunesLibraryFile).Descendants("plist").Elements("dict").Elements("array").Elements("dict")
                                                                    select new XElement("playList",
                                                                        from key in iTunesPlayList.Descendants("key")
                                                                        where key.Value.Replace(" ", "") != "PlaylistItems" // && key.Value.Replace(" ","") != "TrackID"
                                                                        select new XElement(key.Value.Replace(" ", ""),
                                                                            ((XElement)key.NextNode).Value)));

            #region Old playlist load, needed if we don't keep a local member with all iTunes tracks
            //From all available playlists, get a IEnumerable list that only contains user playlists and at least one track!
            //this.iTunesPlayLists = from playList in playListDictionary.Nodes()
            //                       let playListTracks = from trackField in ((XElement)playList).Elements("TrackID")
            //                                            select trackField
            //                       where !defaultiTunesPlayLists.Contains(((XElement)playList).Element("Name").Value) && playListTracks.Count() > 0
            //                       select new iTunesPlaylist()
            //                       {
            //                           Id           = ((XElement)playList).Element("PlaylistID").ToString(string.Empty),
            //                           Name         = ((XElement)playList).Element("Name").ToString(string.Empty),
            //                           iTunesTracks = from track in trackDictionary.Nodes()
            //                                          where (from trackID in ((XElement)playList).Elements("TrackID")
            //                                                 select trackID.Value).Contains(((XElement)track).Element("TrackID").Value)
            //                                          select new iTunesTrack()
            //                                          {
            //                                              Id            = ((XElement)track).Element("TrackID").ToInt(0),
            //                                              Album         = ((XElement)track).Element("Album").ToString(string.Empty),
            //                                              Artist        = ((XElement)track).Element("Artist").ToString(string.Empty),
            //                                              AlbumArtist   = ((XElement)track).Element("AlbumArtist").ToString(string.Empty),
            //                                              BitRate       = ((XElement)track).Element("BitRate").ToInt(0),
            //                                              Comments      = ((XElement)track).Element("Comments").ToString(string.Empty),
            //                                              Composer      = ((XElement)track).Element("Composer").ToString(string.Empty),
            //                                              Genre         = ((XElement)track).Element("Genre").ToString(string.Empty),
            //                                              Kind          = ((XElement)track).Element("Kind").ToString(string.Empty),
            //                                              Location      = ((XElement)track).Element("Location").ToString(string.Empty),
            //                                              Name          = ((XElement)track).Element("Name").ToString(string.Empty),
            //                                              PlayCount     = ((XElement)track).Element("PlayCount").ToInt(0),
            //                                              SampleRate    = ((XElement)track).Element("SampleRate").ToInt(0),
            //                                              Size          = ((XElement)track).Element("Size").ToInt64(0),
            //                                              TotalTime     = ((XElement)track).Element("TotalTime").ToInt64(0),
            //                                              TrackNumber   = ((XElement)track).Element("TrackNumber").ToInt(0)
            //                                          }
            //                       };
            #endregion

            //From all available playlists, get a IEnumerable list that only contains user playlists and at least one track!
            //Load the corresponding tracks from the IEnumerable track list
            this.iTunesPlayLists = (from XElement playList in playListDictionary.Nodes()
                                    let playListTracks = from trackField in playList.Elements("TrackID")
                                                         select trackField
                                    where !this._defaultiTunesPlayLists.Contains(playList.Element("Name").Value) && playListTracks.Count() > 0
                                    select new iTunesPlaylist()
                                    {
                                        Id = playList.Element("PlaylistID").ToString(string.Empty),
                                        PlaylistPersistentID = playList.Element("PlaylistPersistentID").ToString(string.Empty),
                                        ParentPersistentID = playList.Element("ParentPersistentID").ToString(string.Empty),
                                        Name = playList.Element("Name").ToString(string.Empty),
                                        iTunesTracks = from iTunesTrack track in this.iTunesTracks
                                                       where (from trackID in playList.Elements("TrackID")
                                                              select trackID.Value).Contains(track.Id.ToString())
                                                       select track
                                    }).ToList();

            //Add playlist relationships
            foreach (iTunesPlaylist playList in this.iTunesPlayLists)
            {
                //Set the parent of each playlist
                if (!string.IsNullOrEmpty(playList.ParentPersistentID))
                {
                    playList.Parent = this.iTunesPlayLists.Where(item => !string.IsNullOrEmpty(item.PlaylistPersistentID) && item.PlaylistPersistentID.Equals(playList.ParentPersistentID, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
                    //Set the children playlists
                    if (!ReferenceEquals(playList.Parent, null))
                    {
                        if (ReferenceEquals(playList.Parent.Children, null))
                            playList.Parent.Children = new List<iTunesPlaylist>();

                        playList.Parent.Children.Add(playList);
                    }
                }
            }
            #endregion
        }

        /// <summary>
        /// </summary>
        /// <param name="exportConfiguration"></param>
        /// <returns></returns>
        public bool ExportPlaylists(iTunesPlaylistExportConfiguration exportConfiguration)
        {
            switch (exportConfiguration.PlaylistMode)
            {
                case PlaylistModeEnum.Normal:
                case PlaylistModeEnum.Minimal:
                    this.WritePlaylistFile(exportConfiguration);
                    break;
                case PlaylistModeEnum.None:
                    break;
                default:
                    break;
            }

            switch (exportConfiguration.ExportMode)
            {
                case ExportModeEnum.Delete:
                    List<string> exportLocationList = (from iTunesPlaylist playlist in exportConfiguration.iTunesElementList
                                                       from iTunesTrack track in playlist.iTunesTracks
                                                       select Path.GetDirectoryName(this.GetTrackDestinationDirectory(playlist.ValidPlaylistName, track, exportConfiguration))).Distinct().ToList();

                    this.CleanExportLocation(exportLocationList);
                    goto case ExportModeEnum.Normal;
                case ExportModeEnum.Normal:
                    this.ExportTracks(exportConfiguration);
                    break;
                case ExportModeEnum.Synchronize:
                    foreach (iTunesPlaylist playlist in exportConfiguration.iTunesElementList)
                    {
                        List<iTunesTrack> tracks = new List<iTunesTrack>();

                        //TODO: Verify sync mode when tracks are not located underneath the iTunes main library location! ( cfr. ExportTracks method for special cases! )
                        string[] diskDirectories = { };
                        if (Directory.Exists(Path.Combine(exportConfiguration.ExportLocation, playlist.ValidPlaylistName)))
                            diskDirectories = (from diskDirectory in Directory.GetDirectories(Path.Combine(exportConfiguration.ExportLocation, playlist.ValidPlaylistName), "*.*", SearchOption.AllDirectories)
                                               select diskDirectory.Replace(string.Concat(Path.Combine(exportConfiguration.ExportLocation, playlist.ValidPlaylistName), Path.DirectorySeparatorChar), string.Empty)).OrderBy(directory => directory).ToArray<string>();

                        //string[] exportDestinationList = (from iTunesTrack track in playlist.iTunesTracks
                        //                                  select exportConfiguration.CreateSubFolders ? Path.GetDirectoryName(track.Location.Replace(this._iTunesLibraryLocation, string.Empty)) : playlist.ValidPlaylistName
                        //                                     ).Distinct().OrderBy(exportDestination => exportDestination).ToArray<string>();

                        string[] exportDestinationList = (from iTunesTrack track in playlist.iTunesTracks
                                                          select this.GetTrackDestinationDirectory(playlist.ValidPlaylistName, track, exportConfiguration).Replace(string.Concat(Path.Combine(exportConfiguration.ExportLocation, playlist.ValidPlaylistName), Path.DirectorySeparatorChar), string.Empty)
                                                          ).Distinct().OrderBy(exportDestination => exportDestination).ToArray<string>();

                        var directoriesToDelete = (from directoryToDelete in diskDirectories.Except(exportDestinationList)
                                                   select Path.Combine(Path.Combine(exportConfiguration.ExportLocation, playlist.ValidPlaylistName), directoryToDelete)).Distinct().OrderBy(directoryToDelete => directoryToDelete).ToList();

                        foreach (string directoryToDelete in directoriesToDelete)
                        {
                            if (Directory.GetDirectories(directoryToDelete).Length == 0)
                                Directory.Delete(directoryToDelete, true);
                        }

                        foreach (string exportDestination in exportDestinationList)
                        {
                            string[] filesDisk = { };
                            if (Directory.Exists(Path.Combine(exportConfiguration.ExportLocation, Path.Combine(playlist.ValidPlaylistName, exportDestination))))
                                filesDisk = (from fileDisk in Directory.GetFiles(Path.Combine(exportConfiguration.ExportLocation, Path.Combine(playlist.ValidPlaylistName, exportDestination)))
                                             select fileDisk).ToArray<string>();

                            List<iTunesTrack> tracksToVerify = (from iTunesTrack track in playlist.iTunesTracks
                                                                where Path.GetDirectoryName(track.Location.Replace(this._iTunesLibraryLocation, string.Empty)).Equals(exportDestination, StringComparison.OrdinalIgnoreCase)
                                                                select track).Distinct().ToList();

                            tracks.AddRange(tracksToVerify.Except(filesDisk, track => track.FileName, fileDisk => Path.GetFileName(fileDisk)).ToList<iTunesTrack>());
                            var fileListToDelete = filesDisk.Except(tracksToVerify, fileDisk => Path.GetFileName(fileDisk), track => track.FileName);

                            //Delete any files that need to be deleted
                            foreach (string fileToDelete in fileListToDelete)
                                File.Delete(fileToDelete);
                        }
                        int trackCountTotal = tracks.Count;
                        int trackCountCurrent = 1;

                        this.ExportTracks(tracks, exportConfiguration.ExportLocation, playlist.ValidPlaylistName, exportConfiguration.CreateSubFolders, exportConfiguration.BackgroundWorker, trackCountTotal, trackCountCurrent);

                        trackCountCurrent += tracks.Count;
                    }
                    break;
                default:
                    break;
            }

            //TODO: Check export status result with real export!
            return true;
        }

        /// <summary>
        /// </summary>
        /// <param name="playLists"></param>
        /// <returns></returns>
        public bool ExportPlayLists(List<iTunesPlaylist> playLists, string exportLocation)
        {
            foreach (iTunesPlaylist playList in playLists)
            {
                List<iTunesTrack> tracks = (from iTunesTrack track in playList.iTunesTracks
                                            select track).Distinct().ToList();

                //Playlists file names can't contain any / sign, we will convert them to a - sign
                PlaylistWriter playlistWriter = new M3UExtPlaylistWriter(Path.Combine(exportLocation, string.Concat(playList.Name.Replace(@"/", "-"), Constants.FILE_EXTENSION_M3U)), playList);
                this.ExportTracks(tracks, exportLocation, playlistWriter);
                playlistWriter.Close();
            }

            //TODO: Check export status result with real export!
            return true;
        }

        /// <summary>
        /// Export all tracks for the given list of playlists
        /// </summary>
        /// <param name="playLists"></param>
        /// <param name="exportLocation"></param>
        /// <returns></returns>
        public bool ExportPlayLists(List<iTunesPlaylist> playLists, string exportLocation, bool createSubFolders, ExportModeEnum exportMode, BackgroundWorker backgroundWorker)
        {
            #region Dummy code
            //var t1 = from folder in dir.Split(Path.DirectorySeparatorChar)
            //         select Path.Combine(@"C:\Users\gve\Music\iTunes\iTunes Music\", folder);

            //var t2 = from folder in dir.Split(Path.DirectorySeparatorChar)
            //         let folderUri = Path.Combine(@"C:\Users\gve\Music\iTunes\iTunes Music\", folder)
            //         select Directory.CreateDirectory(folderUri);

            ////Get all tracks from all given playlists, but remove doubles!
            //List<iTunesTrack> tracks = (from iTunesPlaylist playList in playLists
            //                            from iTunesTrack track in playList.iTunesTracks
            //                            select track).Distinct().ToList();

            //foreach (iTunesTrack track in tracks)
            //{
            //    string trackLocation = track.Location.Replace(this._iTunesLibraryLocation, string.Empty);
            //    string trackDirectory = Path.GetDirectoryName(trackLocation);

            //    //TODO: Replace the this._iTunesLibrary with the new destination path!
            //    Directory.CreateDirectory(Path.Combine(this._iTunesLibraryLocation, trackDirectory));
            //}
            #endregion

            if (exportMode == ExportModeEnum.Delete)
            {
                List<string> exportLocationList = (from iTunesPlaylist playList in playLists
                                                   let validPlayListName = new string(
                                                       (from char c in playList.Name
                                                        select (_invalidFileChars.Contains(c) ? '-' : c)).ToArray()
                                                      )
                                                   from iTunesTrack track in playList.iTunesTracks
                                                   select Path.Combine(exportLocation, (createSubFolders ? Path.Combine(validPlayListName, Path.GetDirectoryName(track.Location.Replace(this._iTunesLibraryLocation, string.Empty))) : validPlayListName))).Distinct().ToList();

                this.CleanExportLocation(exportLocationList);
            }

            foreach (iTunesPlaylist playList in playLists)
            {
                List<iTunesTrack> tracksForM3U = (from iTunesTrack track in playList.iTunesTracks
                                                  select track).Distinct().ToList();

                List<iTunesTrack> tracks = new List<iTunesTrack>();

                string validPlaylistName = new string(
                                 (from char c in playList.Name
                                  select (_invalidFileChars.Contains(c) ? '-' : c)).ToArray()
                                  );

                switch (exportMode)
                {
                    case ExportModeEnum.Synchronize:
                        //TODO: Verify sync mode when tracks are not located underneath the iTunes main library location! ( cfr. ExportTracks method for special cases! )
                        string[] diskDirectories = { };
                        if (Directory.Exists(Path.Combine(exportLocation, validPlaylistName)))
                            diskDirectories = (from diskDirectory in Directory.GetDirectories(Path.Combine(exportLocation, validPlaylistName), "*.*", SearchOption.AllDirectories)
                                               select diskDirectory.Replace(string.Concat(Path.Combine(exportLocation, validPlaylistName), Path.DirectorySeparatorChar), string.Empty)).OrderBy(directory => directory).ToArray<string>();

                        string[] exportDestinationList = (from iTunesTrack track in playList.iTunesTracks
                                                          select createSubFolders ? Path.GetDirectoryName(track.Location.Replace(this._iTunesLibraryLocation, string.Empty)) : validPlaylistName
                                                             ).Distinct().OrderBy(exportDestination => exportDestination).ToArray<string>();

                        var directoriesToDelete = (from directoryToDelete in diskDirectories.Except(exportDestinationList)
                                                   select Path.Combine(Path.Combine(exportLocation, validPlaylistName), directoryToDelete)).Distinct().OrderBy(directoryToDelete => directoryToDelete).ToList();

                        foreach (string directoryToDelete in directoriesToDelete)
                        {
                            if (Directory.GetDirectories(directoryToDelete).Length == 0)
                                Directory.Delete(directoryToDelete, true);
                        }

                        foreach (string exportDestination in exportDestinationList)
                        {
                            string[] filesDisk = { };
                            if (Directory.Exists(Path.Combine(exportLocation, Path.Combine(validPlaylistName, exportDestination))))
                                filesDisk = (from fileDisk in Directory.GetFiles(Path.Combine(exportLocation, Path.Combine(validPlaylistName, exportDestination)))
                                             select fileDisk).ToArray<string>();

                            List<iTunesTrack> tracksToVerify = (from iTunesTrack track in playList.iTunesTracks
                                                                where Path.GetDirectoryName(track.Location.Replace(this._iTunesLibraryLocation, string.Empty)).Equals(exportDestination, StringComparison.OrdinalIgnoreCase)
                                                                select track).Distinct().ToList();

                            tracks.AddRange(tracksToVerify.Except(filesDisk, track => track.FileName, fileDisk => Path.GetFileName(fileDisk)).ToList<iTunesTrack>());
                            var fileListToDelete = filesDisk.Except(tracksToVerify, fileDisk => Path.GetFileName(fileDisk), track => track.FileName);

                            //Delete any files that need to be deleted
                            foreach (string fileToDelete in fileListToDelete)
                                File.Delete(fileToDelete);
                        }
                        break;
                    default:
                        tracks = tracksForM3U;
                        break;
                }

                int trackCountTotal = tracks.Count;
                int trackCountCurrent = 1;

                PlaylistWriter playlistWriter = new M3UExtPlaylistWriter(Path.Combine(exportLocation, string.Concat(validPlaylistName, Constants.FILE_EXTENSION_M3U)), playList);

                if (exportMode == ExportModeEnum.Synchronize)
                {
                    this.ExportTracks(tracks, exportLocation, validPlaylistName, createSubFolders, backgroundWorker, trackCountTotal, trackCountCurrent);
                    this.ExportTracks(tracksForM3U, exportLocation, validPlaylistName, createSubFolders, playlistWriter);
                }
                else
                    this.ExportTracks(tracks, exportLocation, validPlaylistName, createSubFolders, playlistWriter, backgroundWorker, trackCountTotal, trackCountCurrent);

                trackCountCurrent += tracks.Count;
                playlistWriter.Close();
            }

            //TODO: Check export status result with real export!
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="exportConfiguration"></param>
        /// <returns></returns>
        public bool ExportArtists(iTunesTrackExportConfiguration exportConfiguration)
        {
            switch (exportConfiguration.PlaylistMode)
            {
                case PlaylistModeEnum.Normal:
                case PlaylistModeEnum.Minimal:
                    this.WritePlaylistFile(exportConfiguration);
                    break;
                case PlaylistModeEnum.None:
                    break;
                default:
                    break;
            }

            switch (exportConfiguration.ExportMode)
            {
                case ExportModeEnum.Delete:
                    List<string> exportLocationList = (from iTunesTrack track in exportConfiguration.iTunesElementList
                                                       select Path.GetDirectoryName(this.GetTrackDestinationDirectory(exportConfiguration.ExportLocation, track, exportConfiguration))).Distinct().ToList();

                    this.CleanExportLocation(exportLocationList);
                    goto case ExportModeEnum.Normal;
                case ExportModeEnum.Normal:
                    this.ExportTracks(exportConfiguration);
                    break;
                case ExportModeEnum.Synchronize:
                    List<iTunesTrack> tracks = new List<iTunesTrack>();

                    //TODO: Verify sync mode when tracks are not located underneath the iTunes main library location! ( cfr. ExportTracks method for special cases! )
                    string[] diskDirectories = { };
                    if (Directory.Exists(exportConfiguration.ExportLocation))
                        diskDirectories = (from diskDirectory in Directory.GetDirectories(exportConfiguration.ExportLocation, "*.*", SearchOption.AllDirectories)
                                           select diskDirectory.Replace(string.Concat(exportConfiguration.ExportLocation, Path.DirectorySeparatorChar), string.Empty)).OrderBy(directory => directory).ToArray<string>();

                    //string[] exportDestinationList = { };
                    //if (exportConfiguration.CreateSubFolders)
                    //    exportDestinationList = (from iTunesTrack track in exportConfiguration.iTunesElementList
                    //                             select Path.GetDirectoryName(track.Location.Replace(this._iTunesLibraryLocation, string.Empty))).Distinct().OrderBy(exportDestination => exportDestination).ToArray<string>();

                    string[] exportDestinationList = (from iTunesTrack track in exportConfiguration.iTunesElementList
                                                      select this.GetTrackDestinationDirectory(exportConfiguration.ExportLocation, track, exportConfiguration).Replace(string.Concat(exportConfiguration.ExportLocation, Path.DirectorySeparatorChar), string.Empty)
                                                      ).Distinct().OrderBy(exportDestination => exportDestination).ToArray<string>();

                    var directoriesToDelete = (from directoryToDelete in diskDirectories.Except(exportDestinationList)
                                               select Path.Combine(exportConfiguration.ExportLocation, directoryToDelete)).Distinct().OrderBy(directoryToDelete => directoryToDelete).ToList();

                    foreach (string directoryToDelete in directoriesToDelete)
                    {
                        if (Directory.GetDirectories(directoryToDelete).Length == 0)
                            Directory.Delete(directoryToDelete, true);
                    }

                    foreach (string exportDestination in exportDestinationList)
                    {
                        string[] filesDisk = { };
                        if (Directory.Exists(Path.Combine(exportConfiguration.ExportLocation, exportDestination)))
                            filesDisk = (from fileDisk in Directory.GetFiles(Path.Combine(exportConfiguration.ExportLocation, exportDestination))
                                         select fileDisk).ToArray<string>();

                        List<iTunesTrack> tracksToVerify = (from iTunesTrack track in exportConfiguration.iTunesElementList
                                                            where Path.GetDirectoryName(track.Location.Replace(this._iTunesLibraryLocation, string.Empty)).Equals(exportDestination, StringComparison.OrdinalIgnoreCase)
                                                            select track).Distinct().ToList();

                        tracks.AddRange(tracksToVerify.Except(filesDisk, track => track.FileName, fileDisk => Path.GetFileName(fileDisk)).ToList<iTunesTrack>());
                        var fileListToDelete = filesDisk.Except(tracksToVerify, fileDisk => Path.GetFileName(fileDisk), track => track.FileName);

                        //Delete any files that need to be deleted
                        foreach (string fileToDelete in fileListToDelete)
                            File.Delete(fileToDelete);
                    }
                    int trackCountTotal = tracks.Count;
                    int trackCountCurrent = 1;

                    this.ExportTracks(tracks, exportConfiguration.ExportLocation, string.Empty, exportConfiguration.CreateSubFolders, exportConfiguration.BackgroundWorker, trackCountTotal, trackCountCurrent);

                    trackCountCurrent += tracks.Count;
                    break;
                default:
                    break;
            }

            //TODO: Check export status result with real export!
            return true;
        }
        
        /// <summary>
        /// Export  all tracks for the given list of album artists
        /// </summary>
        /// <param name="artists"></param>
        /// <param name="exportLocation"></param>
        /// <returns></returns>
        public bool ExportArtists(List<string> artists, string exportLocation, bool createSubFolders, ExportModeEnum exportMode, BackgroundWorker backgroundWorker)
        {
            //When searching for all given album artists, we will not use the .Contains() method from the List<T> because it's case sensitive!
            //List<iTunesTrack> tracks = (from iTunesTrack track in this.iTunesTracks
            //                            where artists.Contains(track.AlbumArtist)
            //                            select track).Distinct().ToList();

            if (exportMode == ExportModeEnum.Delete)
            {
                List<string> exportLocationList = (from iTunesTrack track in this.iTunesTracks
                                                   where artists.Exists(delegate(string argument)
                                                   {
                                                       return argument.Equals(track.AlbumArtist, StringComparison.OrdinalIgnoreCase);
                                                   })
                                                   select Path.Combine(exportLocation, (createSubFolders ? Path.Combine(Constants.EXPORTLOCATIONNAME_ARTISTS, Path.GetDirectoryName(track.Location.Replace(this._iTunesLibraryLocation, string.Empty))) : Constants.EXPORTLOCATIONNAME_ARTISTS))).Distinct().ToList();

                this.CleanExportLocation(exportLocationList);
            }

            List<iTunesTrack> tracks = (from iTunesTrack track in this.iTunesTracks
                                        where artists.Exists(delegate(string argument)
                                        {
                                            return argument.Equals(track.AlbumArtist, StringComparison.OrdinalIgnoreCase);
                                        })
                                        select track).Distinct().ToList();

            int trackCountTotal = tracks.Count;
            int trackCountCurrent = 1;

            PlaylistWriter playlistWriter = new M3UExtPlaylistWriter(Path.Combine(exportLocation, string.Concat("Artists", ".m3u")), null);
            this.ExportTracks(tracks, exportLocation, Constants.EXPORTLOCATIONNAME_ARTISTS, createSubFolders, playlistWriter, backgroundWorker, trackCountTotal, trackCountCurrent);
            playlistWriter.Close();

            //TODO: Check export status result with real export!
            return true;
        }
        #endregion
    }
}